<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>Advanced controls</title>
<link rel="stylesheet" href="/cfg/format.css" type="text/css">
<meta http-equiv="content-type" content="text/html; charset=utf-8">
<meta name="keywords" content="winapi, Windows API, C, no MFC, Advanced Windows Controls">
<meta name="description" content="In this part of the Winapi tutorial we cover advanced controls.">
<meta name="language" content="en">
<meta name="author" content="Jan Bodnar">
<meta name="distribution" content="global">

<script type="text/javascript" src="/lib/jquery.js"></script>
<script type="text/javascript" src="/lib/common.js"></script>

</head>

<body>

<div class="container">

<div id="wide_ad" class="ltow">
<script type="text/javascript"><!--
google_ad_client = "pub-9706709751191532";
/* 160x600, August 2011 */
google_ad_slot = "2484182563";
google_ad_width = 160;
google_ad_height = 600;
//-->
</script>
<script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js">
</script>
</div>

<div class="content">


<a href="/" title="Home">Home</a>&nbsp;
<a href="..">Contents</a>


<h1>Advanced Windows Controls</h1>


<p>
In this section of the Winapi C tutorial, we will talk about more advanced windows controls.
</p>

<script type="text/javascript"><!--
google_ad_client = "ca-pub-9706709751191532";
/* LargeSquare */
google_ad_slot = "5070696322";
google_ad_width = 336;
google_ad_height = 280;
//-->
</script>
<script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js">
</script>


<h2>Tab Control</h2>

<p>
A tab control joins multiple windows with corresponding tabs.
</p>

<pre class="code">
#include &lt;windows.h&gt;
#include &lt;commctrl.h&gt;

#define ID_TABCTRL 1
#define EDIT 2
#define BTN_ADD 3
#define BTN_DEL 4
#define BTN_DELALL 5

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
HWND hTab, hEdit;
HINSTANCE g_hinst;

int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, 
    LPSTR lpCmdLine, int nCmdShow )
{
  MSG  msg ;    
  WNDCLASS wc = {0};
  wc.lpszClassName = TEXT( "Application" );
  wc.hInstance     = hInstance ;
  wc.hbrBackground = GetSysColorBrush(COLOR_3DFACE);
  wc.lpfnWndProc   = WndProc ;
  wc.hCursor       = LoadCursor(0,IDC_ARROW);

  g_hinst = hInstance;
  
  RegisterClass(&amp;wc);
  CreateWindow( wc.lpszClassName, TEXT("Tab Control"),
                WS_OVERLAPPEDWINDOW | WS_VISIBLE,
                100, 100, 380, 230, 0, 0, hInstance, 0);  

  while( GetMessage(&amp;msg, NULL, 0, 0)) {
    TranslateMessage(&amp;msg);
    DispatchMessage(&amp;msg);
  }
  return (int) msg.wParam;
}


LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)

{

   TCITEM tie;
   TCHAR text[250];
   LRESULT count, id;
   INITCOMMONCONTROLSEX icex;

   switch(msg) {

       case WM_CREATE:
       
           icex.dwSize = sizeof(INITCOMMONCONTROLSEX);
           icex.dwICC = ICC_TAB_CLASSES;
           InitCommonControlsEx(&amp;icex);

          hTab = CreateWindow(WC_TABCONTROL, NULL, WS_CHILD | WS_VISIBLE,
              0, 0, 200, 150, hwnd,(HMENU) ID_TABCTRL, g_hinst, NULL);

          hEdit = CreateWindow("edit",NULL,WS_CHILD | WS_VISIBLE | WS_BORDER,
              250, 20, 100, 25, hwnd, (HMENU) EDIT, g_hinst, NULL);
  
          CreateWindow("button","add", WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,
              250, 50, 100, 25, hwnd, (HMENU) BTN_ADD, g_hinst, NULL);

          CreateWindow("button", "del", WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,
              250, 80, 100, 25, hwnd, (HMENU) BTN_DEL, g_hinst, NULL);

          CreateWindow("button","delall", WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,
              250, 110, 100, 25, hwnd, (HMENU) BTN_DELALL, g_hinst, NULL);
          break;

       case WM_COMMAND:

           switch(LOWORD(wParam)) {

               case BTN_ADD:
                   GetWindowText(hEdit, text, 250);

                   if (lstrlen(text) !=0 ) {
                       tie.mask = TCIF_TEXT;
                       tie.pszText = text;
                       count = SendMessage(hTab, TCM_GETITEMCOUNT, 0, 0);
                       SendMessage(hTab, TCM_INSERTITEM, count, 
                           (LPARAM) (LPTCITEM) &amp;tie);
                   }
                   break;

               case BTN_DEL:
                   id = SendMessage(hTab, TCM_GETCURSEL, 0, 0);
                   if (id != -1) {
                       SendMessage(hTab, TCM_DELETEITEM, 0, id);
                   }
                   break;

               case BTN_DELALL:
                   SendMessage(hTab, TCM_DELETEALLITEMS, 0, 0);
                   break;
           } 
           break;

       case WM_DESTROY:
           PostQuitMessage(0);
           break;
   }
   return(DefWindowProc(hwnd, msg, wParam, lParam));
}
</pre>

<p>
In our example, we use one tab control, one edit control and three buttons. 
We will dynamically create and delete new tabs on the
tab control.
</p>

<pre class="explanation">
 hTab = CreateWindow(WC_TABCONTROL, NULL, WS_CHILD | WS_VISIBLE,
     0, 0, 200, 150, hwnd,(HMENU) ID_TABCTRL, g_hinst, NULL);
</pre>

<p>
We use the <i>WC_TABCONTROL</i> window class to create a tab control.
</p>


<pre class="explanation">
if (lstrlen(text) !=0 ) {
    tie.mask = TCIF_TEXT;
    tie.pszText = text;
    count = SendMessage(hTab, TCM_GETITEMCOUNT, 0, 0);
    SendMessage(hTab, TCM_INSERTITEM, count, 
        (LPARAM) (LPTCITEM) &amp;tie);
}
</pre>

<p>
To add a new tab, we fill the <i>TCITEM</i> structure. We provide the type 
of data to be set (in our case <i>TCIF_TEXT</i>) and the actual text.
Then we send two messages. The first message will get the number of tabs 
on the tab control. It will be used in the second message. The second one
will insert a new tab to the control, using the <i>count</i> variable
 and the <i>TCIF_TEXT</i> structure.
</p>

 

<pre class="explanation">
 id = SendMessage(hTab, TCM_GETCURSEL, 0, 0);
 if (id != -1) {
     SendMessage(hTab, TCM_DELETEITEM, 0, id);
 }
</pre>

<p>
To delete an specific tab, we need the current selected tab. We figure it 
out by sending the <i>TCM_GETCURSEL</i> message to the tab control.
To actually delete the tab, we send the <i>TCM_DELETEITEM</i>, specifying 
the item to be deleted in the <i>wParam</i> parameter.
</p>

<pre class="explanation">
 SendMessage(hTab, TCM_DELETEALLITEMS, 0, 0);
</pre>

<p>
To delete all tabs from the tab control, we send the <i>TCM_DELETEALLITEMS</i> message.
</p>

<br>
<img src="/img/gui/winapi/tabcontrol.jpg" alt="tab control">
<div class="figure">Figure: Tab Control</div>

<h2>ListBox</h2>

<p>
A list box is a simple list control, from which user can select one or 
more items. Selected items are marked.
</p>

<pre class="code">
#include &lt;windows.h&gt;
#include &lt;strsafe.h&gt;

#define IDC_LIST 1
#define IDC_STATIC 2

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);

HINSTANCE g_hinst;

typedef struct 
{ 
    TCHAR name[30]; 
    TCHAR job[20]; 
    int age; 

} Friends; 

Friends friends[] = 
{ 
    {TEXT("Erika"), TEXT("waitress"), 18}, 
    {TEXT("Thomas"), TEXT("programmer"), 25}, 
    {TEXT("George"), TEXT("police officer"), 26}, 
    {TEXT("Michael"), TEXT("producer"), 38}, 
    {TEXT("Jane"), TEXT("steward"), 28}, 
}; 

int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, 
    LPSTR lpCmdLine, int nCmdShow )
{
  MSG  msg ;    
  WNDCLASS wc = {0};
  wc.lpszClassName = TEXT( "Application" );
  wc.hInstance     = hInstance;
  wc.hbrBackground = GetSysColorBrush(COLOR_3DFACE);
  wc.lpfnWndProc   = WndProc;
  wc.hCursor       = LoadCursor(0, IDC_ARROW);

  g_hinst = hInstance;
  
  RegisterClass(&amp;wc);
  CreateWindow( wc.lpszClassName, TEXT("List Box"),
                WS_OVERLAPPEDWINDOW | WS_VISIBLE,
                100, 100, 340, 200, 0, 0, hInstance, 0);  

  while( GetMessage(&amp;msg, NULL, 0, 0)) {
    TranslateMessage(&amp;msg);
    DispatchMessage(&amp;msg);
  }
  return (int) msg.wParam;
}


LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{

    static HWND hwndList, hwndStatic;
    int i, sel;
    TCHAR buff[100];

    switch(msg) {

        case WM_CREATE:
       
            hwndList = CreateWindow(TEXT("listbox") , NULL, WS_CHILD | WS_VISIBLE | LBS_NOTIFY,
               10, 10, 150, 120, hwnd,(HMENU) IDC_LIST, g_hinst, NULL);

            hwndStatic = CreateWindow(TEXT("static") , NULL, WS_CHILD | WS_VISIBLE,
               200, 10, 120, 45, hwnd,(HMENU) IDC_STATIC, g_hinst, NULL);

            for (i = 0; i < ARRAYSIZE(friends); i++)  { 
                 SendMessage(hwndList, LB_ADDSTRING, 0, (LPARAM) friends[i].name);                                 
            } 

            break;
 
        case WM_COMMAND:
            if (LOWORD(wParam) == IDC_LIST) {        
                if (HIWORD(wParam) == LBN_SELCHANGE) {                   
                    sel = (int) SendMessage(hwndList, LB_GETCURSEL, 0, 0);
                    StringCbPrintf(buff, ARRAYSIZE(buff), TEXT("Job: %s\nAge: %d"), 
                        friends[sel].job, friends[sel].age);
                    SetWindowText(hwndStatic, buff);
               }
            }            
            break;

        case WM_DESTROY:
            PostQuitMessage(0);
            break;
    }
    return (DefWindowProc(hwnd, msg, wParam, lParam));
}
</pre>

<p>
In this example, we display a list box control and a static text control. 
By selecting a person from a list box, we display his job and age
in the static control. 
</p>

<pre class="explanation">
 hwndList = CreateWindow(TEXT("listbox") , NULL, WS_CHILD | WS_VISIBLE | LBS_NOTIFY,
     10, 10, 150, 120, hwnd,(HMENU) IDC_LIST, g_hinst, NULL);
</pre>

<p>
A basic list box is created with <i>listbox</i> window class and <i>LBS_NOTIFY</i> message.
</p>

<pre class="explanation">
 for (i = 0; i < ARRAYSIZE(friends); i++)  { 
     SendMessage(hwndList, LB_ADDSTRING, 0, (LPARAM) friends[i].name);                                 
 } 
</pre>

<p>
 We fill the list box. To do this, we send multiple <i>LB_ADDSTRING</i> 
messages to the list box control. 
</p>

<pre class="explanation">
 if (HIWORD(wParam) == LBN_SELCHANGE) {                   
  sel = (int) SendMessage(hwndList, LB_GETCURSEL, 0, 0);
  StringCbPrintf(buff, ARRAYSIZE(buff), TEXT("Job: %s\nAge: %d"), 
      friends[sel].job, friends[sel].age);
  SetWindowText(hwndStatic, buff);
 }
</pre>

<p>
If we select an item from a list box, the window procedure receives a 
<i>LBN_SELCHANGE</i> message. First we determine the currently selected item by 
sending a <i>LB_GETCURSEL</i> message to the list box. Then we copy the 
job name and age from the friends structure to the buff variable. 
Finally, we set the static text with <i>SetWindowText()</i> function call.
</p>


<img src="/img/gui/winapi/listbox.jpg" alt="list box">
<div class="figure">Figure: List Box</div>


<p>
In this part of the Winapi tutorial, we have covered advanced Winapi controls. 
</p>


<div class="center"> 
<script type="text/javascript"><!--
google_ad_client = "pub-9706709751191532";
/* horizontal */
google_ad_slot = "1734478269";
google_ad_width = 468;
google_ad_height = 60;
//-->
</script> 
<script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js"> 
</script> 
</div> 
<br> 

<div class="botNav, center">
<span class="botNavItem"><a href="/">Home</a></span> ‡ <span class="botNavItem"><a href="..">Contents</a></span> ‡
<span class="botNavItem"><a href="#">Top of Page</a></span>
</div>


<div class="footer">
<div class="signature">
<a href="/">ZetCode</a> last modified July 26, 2007  <span class="copyright">&copy; 2007 - 2013 Jan Bodnar</span>
</div>
</div>

</div> <!-- content -->

</div> <!-- container -->

</body>
</html>

